package com.sdu.douban_comment.service;

import com.alibaba.fastjson2.JSONObject;
import com.sdu.douban_comment.dao.CommentDao;
import com.sdu.douban_comment.entity.Film;
import com.sdu.douban_comment.entity.RequestParam;
import com.sdu.douban_comment.utils.DataUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.text.DecimalFormat;
import java.util.*;

import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;


@Service
public class CommentSerivce {


    @Autowired
    private MongoTemplate template;

    @Autowired
    private CommentDao commentDao;


    public int importData() throws IOException {

        List<Film> films = DataUtils.convert();

        Collection<Film> films1 = template.insertAll(films);
        return films1.size();
    }

    public List<Film> find(){
        List<Film> all = template.findAll(Film.class);
        return all;
    }

    public long count(){
        long count = template.count(new Query(), Film.class);
        return count;
    }

    public double findScoreMax() {
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.group().max("comment.score").as("score")
        );

        AggregationResults<JSONObject> result = template.aggregate(aggregation, "test_comment", JSONObject.class);
        JSONObject object = result.getUniqueMappedResult();
        return Double.parseDouble(object.getString("score"));
    }
    public double findScoreMin() {
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.group().min("comment.score").as("score")
        );

        AggregationResults<JSONObject> result = template.aggregate(aggregation, "test_comment", JSONObject.class);
        JSONObject object = result.getUniqueMappedResult();
        return Double.parseDouble(object.getString("score"));
    }
    public double findScoreAvg() {
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.group().avg("comment.score").as("score")
        );

        AggregationResults<JSONObject> result = template.aggregate(aggregation, "test_comment", JSONObject.class);
        JSONObject object = result.getUniqueMappedResult();
        DecimalFormat df = new DecimalFormat("0.0");
        return Double.parseDouble(df.format(Double.parseDouble(object.getString("score"))));
    }

    public double findScoreMed() {
        long count = count();
        if(count%2==1){
            count++;
        }
        int index=(int)count/2;
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.sort(Sort.Direction.ASC, "comment.score"),
                Aggregation.group()
                        .push("comment.score").as("scores"),
                project()
                        .and(ArrayOperators.arrayOf("scores")
                                .elementAt(index
                                )
                        ).as("score")
        );

        AggregationResults<JSONObject> result = template.aggregate(aggregation, "test_comment", JSONObject.class);
        JSONObject object = result.getUniqueMappedResult();
        return Double.parseDouble(object.getString("score"));
    }

    public Map<String, Integer> countFilmByYear() {

        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.project()
                        .andExpression("substr(releaseDateFormat, 0, 4)").as("year"),
                Aggregation.group("year").count().as("count"),
                Aggregation.project("year","count").and("_id").as("year").andExclude("_id"),
                Aggregation.sort(Sort.Direction.ASC,"year")
        );

        AggregationResults<JSONObject> results = template.aggregate(aggregation, "test_comment",JSONObject.class);
        List<JSONObject> countJ=results.getMappedResults();
        Map<String, Integer> map=new LinkedHashMap<>();
        countJ.forEach(obj -> {
            String year = obj.getString("year");
            Integer count = obj.getInteger("count");
            if ("0000".equals(year)) year = "未知";
            map.put(year, count);
        });
        return map;

    }

    public Map<String, Integer> countFilmByMonth(String date) {
        Criteria criteria = Criteria.where("releaseDateFormat").regex("^"+date);
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.project()
                        .andExpression("substr(releaseDateFormat, 5, 2)").as("month"),
                Aggregation.group("month").count().as("count"),
                Aggregation.project("month","count").and("_id").as("month").andExclude("_id"),
                Aggregation.sort(Sort.Direction.ASC,"month")
        );
        AggregationResults<JSONObject> results = template.aggregate(aggregation, "test_comment",JSONObject.class);
        List<JSONObject> countJ=results.getMappedResults();
        Map<String, Integer> map=new LinkedHashMap<>();
        countJ.forEach(obj -> {
            String month = obj.getString("month");
            Integer count = obj.getInteger("count");
            if ("00".equals(month)) month = "未知";
            map.put(month, count);
        });
        return map;
    }

    public Map<String, Integer> countFileByType(String[] locations) {

        Criteria match = new Criteria();
        if(locations!=null&&locations.length>0){
            match=Criteria.where("productionCty").in(locations);
        }

        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(match),
                Aggregation.unwind("type"),
                Aggregation.group("type").count().as("count"),
                Aggregation.project("type","count").and("_id").as("type").andExclude("_id"),
                Aggregation.sort(Sort.Direction.ASC,"count")
        );

        AggregationResults<JSONObject> results = template.aggregate(aggregation, "test_comment", JSONObject.class);
        List<JSONObject> resultJ = results.getMappedResults();
        Map<String, Integer> result = new LinkedHashMap<>();
        resultJ.forEach(obj -> {
            String type = obj.getString("type");
            if("".equals(type)){
                type="未知";
            }
            Integer count = obj.getInteger("count");
            result.put(type, count);
        });

        return result;
    }

    public String[] showTypes() {
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.unwind("type"),
                Aggregation.group().addToSet("type").as("types")
        );
        AggregationResults<JSONObject> response = template.aggregate(aggregation, "test_comment", JSONObject.class);
        JSONObject resultJ = response.getUniqueMappedResult();
        String[] types = resultJ.getJSONArray("types").toArray(String.class);
        return types;
    }

    public String[] showProductCty() {
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.unwind("productionCty"),
                Aggregation.group().addToSet("productionCty").as("cities")
        );
        AggregationResults<JSONObject> response = template.aggregate(aggregation, "test_comment", JSONObject.class);
        JSONObject resultJ = response.getUniqueMappedResult();
        String[] types = resultJ.getJSONArray("cities").toArray(String.class);
        return types;
    }

    public Map<String, Integer> showHotActors() {
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.unwind("actors"),
                Aggregation.group("actors").count().as("count"),
                Aggregation.sort(Sort.Direction.DESC, "count"),
                project("actors", "count").and("_id").as("actors").andExclude("_id"),
                Aggregation.limit(11)
        );

        AggregationResults<JSONObject> results = template.aggregate(aggregation, "test_comment", JSONObject.class);
        List<JSONObject> resultJ = results.getMappedResults();
        Map<String, Integer> result = new LinkedHashMap<>();
        resultJ.forEach(obj -> {
            String actors = obj.getString("actors");
            if(!"".equals(actors)) {
                Integer count = obj.getInteger("count");
                result.put(actors, count);
            }
        });
        return result;
    }

    public Map<String, Integer> countFileByCity(String[] types) {
        Criteria match =new Criteria();
        if(types!=null&&types.length>0){
            match=Criteria.where("type").in(types);
        }
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(match),
                Aggregation.unwind("productionCty"),
                Aggregation.group("productionCty").count().as("count"),
                Aggregation.project("productionCty","count").and("_id").as("productionCty").andExclude("_id"),
                Aggregation.sort(Sort.Direction.ASC, "count")
        );

        AggregationResults<JSONObject> results = template.aggregate(aggregation, "test_comment", JSONObject.class);
        List<JSONObject> resultJ = results.getMappedResults();
        Map<String, Integer> result = new LinkedHashMap<>();
        resultJ.forEach(obj -> {
            String city = obj.getString("productionCty");
            Integer count = obj.getInteger("count");
            result.put(city, count);
        });

        return result;

    }

    public Page<Film> showFilms(RequestParam request) {
        String sort= request.getOrderBy()==0?"comment.score":"releaseDate";
        Pageable page = PageRequest.of(request.getPageNum(), request.getPageSize(),Sort.by(Sort.Order.desc(sort)));
        Page<Film> result = commentDao.findByProductionCtyInAndTypeIn(request.getLocations(), request.getTypes(),page);

        return result;
    }
}
